/*
 * Copyright (c) 2011 Imre Fazekas.
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the Brillien nor the names of its
 * terms and concepts may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.brillien.core.management.component;

import com.vii.brillien.core.management.component.structure.Configuration;
import com.vii.brillien.core.management.component.structure.PresenceDef;
import com.vii.brillien.ignition.classloading.PresenceClassLoader;
import com.vii.brillien.kernel.axiom.atomic.PresenceManager;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Service class to store meta information about components' deployment descriptors
 */
public class MetaServices {

    /**
     * Stores component archieves' deployment descriptors' ConfigurationTree strucures
     */
    protected static ConcurrentHashMap<String, Configuration>           metaInfos;

    /**
     * This map connect the deployment archieves's with the PresenceManagers of presences defined by them
     */
    protected static ConcurrentHashMap<String, List>                    sourceInfo;

    /**
     * This map connect the deployment archieves to classloader instances used to load presences defined by the component archiv
     */
    protected static ConcurrentHashMap<String, PresenceClassLoader>     classLoaders;

    /**
     * This map connect the deployment archieves to classloader instances used to load presences defined by the component archiv
     */
    protected static ConcurrentHashMap<String, ConcurrentLinkedQueue>   presences;

    /**
     * Initializer method
     * Invoked by starting the application server
     */
    public static void init(){
        metaInfos       = new ConcurrentHashMap<String, Configuration>();
        sourceInfo      = new ConcurrentHashMap<String, List>();
        classLoaders    = new ConcurrentHashMap<String, PresenceClassLoader>();
        presences       = new ConcurrentHashMap<String, ConcurrentLinkedQueue>();
        //sourceInfo = new HashMap<PresenceManager, String>();
    }

    /**
     * Registers ConfigurationReader instances to a given deployment archieve's jar file
     * @param jarReferenceName filepath to the deployment archieve's  jar file
     * @param loader Classlaoder instance for the given application archive
     */
    public static void registerClassloader(String jarReferenceName, PresenceClassLoader loader){
        classLoaders.put( jarReferenceName, loader );
        presences.put( jarReferenceName, new ConcurrentLinkedQueue<String>() );
    }

    /**
     * Registers ConfigurationReader instances to a given deployment archieve's jar file
     * @param jarReferenceName filepath to the deployment archieve's  jar file
     * @param config Json configuration structure
     */
    public static void registerMetaInfo(String jarReferenceName, Configuration config){
        metaInfos.put( jarReferenceName, config );
    }

    public static boolean hasMetaInfo( String jarReferenceName ){
        return metaInfos.containsKey( jarReferenceName );
    }

    public static List<PresenceManager> getSourceInfo(String jarReferenceName ){
        List<PresenceManager> list = sourceInfo.get( jarReferenceName );
        return list;
    }

    public static void registerSourceInfo(String jarReferenceName, PresenceManager manager ){
        if( !sourceInfo.containsKey( jarReferenceName ) )
            sourceInfo.put( jarReferenceName, new LinkedList<PresenceManager>() );

        sourceInfo.get( jarReferenceName ).add( manager );
    }

    /**
     * Getter method for DescriptionInfo of a component by deployment archieve's name
     * @param jarReferenceName deployment archieve's name
     * @return Description Info
     */
    public static double getDescriptionInfo(String jarReferenceName){
        return !hasMetaInfo( jarReferenceName ) ? 1.0 : metaInfos.get( jarReferenceName ).getDescriptionVersion();
    }

    /**
     * Getter method for Vendor name of a component by deployment archieve's name
     * @param jarReferenceName deployment archieve's name
     * @return Vendor name
     */
    public static String getVendorName(String jarReferenceName){
        return !hasMetaInfo( jarReferenceName ) ? "Not defined." : metaInfos.get( jarReferenceName ).getVendor().getName( );
    }

    /**
     * Getter method for Vendor homepage of a component by deployment archieve's name
     * @param jarReferenceName deployment archieve's name
     * @return Vendor homepage
     */
    public static String getVendorHomePage(String jarReferenceName){
        return !hasMetaInfo( jarReferenceName ) ? "Not defined." : metaInfos.get( jarReferenceName ).getVendor().getHomePage();
    }

    /**
     * Getter method for Vendor description of a component by deployment archieve's name
     * @param jarReferenceName deployment archieve's name
     * @return Vendor description
     */
    public static String getVendorDescription(String jarReferenceName){
        return !hasMetaInfo( jarReferenceName ) ? "Not defined." : metaInfos.get(jarReferenceName).getVendor().getDescription();
    }

    /**
     * Gets the list of clone presences to be published
     * @param jarReferenceName reference name of deployment archieve
     * @return list of clone presences. key is the cloneName, value is the originalName
     */
    public static Map<String, String> getClonePresences(String jarReferenceName){
        if( !hasMetaInfo( jarReferenceName ) ) return null;

        HashMap<String, String> res = new HashMap<String, String>();
        for( PresenceDef def : metaInfos.get( jarReferenceName ).getActivate().getPresences() )
            if( def.getCloneName() != null && def.getOriginalName() != null )
                res.put( def.getCloneName(), def.getOriginalName() );

        return res;
    }

    /**
     * Retrieves the classNames defined by the deployment config file.
     */
    public static List<String> getPresences(String jarReferenceName){
        if( !hasMetaInfo( jarReferenceName ) ) return null;

        LinkedList<String> res = new LinkedList<String>();
        for( PresenceDef def : metaInfos.get( jarReferenceName ).getActivate().getPresences() ){
            res.add( def.getClassName() );
        }
        return res;
    }

    /**
     * In the deployment archieve, all Presences may have parameter map used during initialization
     * @param jarReferenceName reference name of deployment archieve
     * @param className name of the Presence
     * @return parameter map of the component
     */
    public static Map<String, Object> getPresenceParameters(String jarReferenceName, String className){
        if( !hasMetaInfo( jarReferenceName ) ) return null;

        for( PresenceDef def : metaInfos.get( jarReferenceName ).getActivate().getPresences() ){
            if( def.getClassName() != null && def.getClassName().equals( className ) )
                return def.getParameters();
        }

        return null;
    }


    /**
     * Gets all published Presences put in the given component archieve
     * @param jarReferenceName reference name of deployment archieve
     * @return published presences
     */
    public static List<String> getActivatedPresences(String jarReferenceName){
        Object[] names = presences.get( jarReferenceName ).toArray( );
        List<String> activated = new LinkedList<String>();
        for( Object o : names )
            activated.add( o.toString() );
        return activated;
    }


    /**
     * Gets the main class (Activator) defined by the deployment descriptor of the given component archieve
     * @param jarReferenceName reference name of deployment archieve
     * @return main class
     */
    public static String getMainClass(String jarReferenceName){
        if( !hasMetaInfo( jarReferenceName ) ) return null;

        return metaInfos.get( jarReferenceName ).getActivate().getActivator();
    }

    /**
     * Retrieves the name of the deployment archieve containing the Presence type managed by the given PresenceManager
     */
    public static String getJarReferenceName( PresenceManager manager ){
        for( String key : sourceInfo.keySet() )
            for( Object o : sourceInfo.get( key ) )
                if( o.equals( manager ) )
                    return key;
        return null;
    }

    /**
     * Retrieves the classloader associated to the given component archive
     */
    public static PresenceClassLoader getClassLoader( String jarName ){
        return classLoaders.get( jarName );
    }

}

